/*
 * Copyright 2008-2009 ArtisanLogic.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.artisanlogic.jingo.parser;

import java.util.SortedSet;
import java.util.TreeSet;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.Tree;

import com.artisanlogic.jingo.assembler.Dependency;
import com.artisanlogic.js.JavaScriptLexer;
import com.artisanlogic.js.JavaScriptParser;

// =====================================================================================================================
// DefaultParser.java                                                                            Created on Feb 18, 2009
// ---------------------------------------------------------------------------------------------------------------------
/**
 * This class is an Parser implementation based on ANTLR.
 * 
 * @author sean@artisanlogic.com
 * @author john@artisanlogic.com
 * @since 0.9-Beta
 */
// =====================================================================================================================
public class AntlrParser implements Parser {
  
  // -------------------------------------------------------------------------------------------------------------------
  // public instance methods
  // -------------------------------------------------------------------------------------------------------------------

	/* (non-Javadoc)
	 * @see com.artisanlogic.jingo.parser.Parser#parse(java.lang.String, com.artisanlogic.jingo.parser.Handler, 
	 *   java.lang.String)
	 */
	public void parse(String source, Handler handler, String defaultRepo) {
		JavaScriptLexer lexer = new JavaScriptLexer(new ANTLRStringStream(source));
		JavaScriptParser parser = new JavaScriptParser(new CommonTokenStream(lexer));
		try {
			handleNode((Tree)parser.program().getTree(), handler, defaultRepo, 0);
		} catch (RecognitionException ex) {
			throw new RuntimeException(ex);
		}
	}
	
	// -------------------------------------------------------------------------------------------------------------------
  // private instance methods
  // -------------------------------------------------------------------------------------------------------------------
	
  private void handleNode(Tree node, Handler handler, String defaultRepo, int depth) {
		if(node.getType() == JavaScriptParser.CALL) {
			handleCall(node, handler, defaultRepo, depth);
		} else {
			handleChildren(node, handler, defaultRepo, depth);
		}
	}

  private void handleChildren(Tree node, Handler handler, String defaultRepo, int depth) {
		for(int i = 0; i < node.getChildCount(); i++) {
			handleNode(node.getChild(i), handler, defaultRepo, depth);
		}
	}

	private void handleCall(Tree call, Handler handler, String defaultRepo, int depth) {
		if(isJingoDeclaration(call.getChild(0))) {
			handleConfig(call.getChild(1).getChild(0), handler, defaultRepo, ++depth);
		} else {
			handleChildren(call, handler, defaultRepo, depth);
		}
	}
	
	private boolean isJingoDeclaration(Tree node) {
		if(node.getType() != JavaScriptParser.BYFIELD) {
			return false;
		}
		StringBuffer declaration = new StringBuffer(); 
		for(int i = 0; i < node.getChildCount(); i++) {
			declaration.append(node.getChild(i).toString());
			declaration.append('.');
		}
		declaration.deleteCharAt(declaration.length() - 1);
		return "jingo.anonymous".equals(declaration.toString()) || "jingo.declare".equals(declaration.toString());
	}
	
	private void handleConfig(Tree config, Handler handler, String defaultRepo, int depth) {
		for(int i = 0; i < config.getChildCount(); i++) {
			if(isRequire(config.getChild(i))) {
				handleRequire(config.getChild(i).getChild(1), handler, defaultRepo, depth);
			} else {
				handleChildren(config.getChild(i), handler, defaultRepo, depth);
			}
		}
	}

	private boolean isRequire(Tree namedValue) {
		return "require".equals(namedValue.getChild(0).toString());
	}
	
	private void handleRequire(Tree require, Handler handler, String defaultRepo, int depth) {
		SortedSet<Dependency> dependencies = new TreeSet<Dependency>();
		for(int i = 0; i < require.getChildCount(); i++) {
			dependencies.add(Dependency.valueOf(require.getChild(i).getChild(0).toString().replace("'", ""), defaultRepo));
		}
		if(!dependencies.isEmpty()) {
			handler.handleDependencies(depth, dependencies);
		}
	}

}
